#include "KKApplication.h"
#include "KKComm.h"
#include "KKRenderI.h"
#include "KKUIBase.h"
#include "KKWindow.h"
#include <cmath>
#include <stdio.h>
#include <vector>

KKApplication *g_app = nullptr;

KKApplication *KKApplication::GetInstance()
{
    if (g_app == nullptr)
    {
        g_app = new KKApplication();
    }
    return g_app;
}

void KKApplication::Quit() { PostQuitMessage(0); }
void KKApplication::Run()
{
    MSG msg;
    // 主消息循环:
    while (::GetMessageW(&msg, nullptr, 0, 0))
    {
        ::TranslateMessage(&msg);
        ::DispatchMessageW(&msg);
    }
}

KKWindow *KKApplication::WindowCreate(int width, int height, const char *title, int flags)
{
    auto win = KKWindow::CreateWin(width, height, title, flags);
    m_winList.push_back(win);
    return win;
}

void KKApplication::WindowRemove(KKWindow *win)
{
    auto it = std::find(m_winList.begin(), m_winList.end(), win);
    if (it != m_winList.end())
    {
        m_winList.erase(it);
    }
}

void KKApplication::OnEvent(KKWindow *win, KKEvent &event)
{
    switch (event.msg)
    {
    case KK_WIN_MSG_MOUSE_MOVE:
        break;
    case KK_WIN_MSG_CREATE:
    {
        Log(win, "KK_WIN_MSG_CREATE\r\n");
    }
    break;
    case KK_WIN_MSG_PAINT:
    {
        _RenderWinUI(win);
    }
    break;
    case KK_WIN_MSG_CLOSE:
    {
        // remove window and release render
        win->GetRender()->Release();
        WindowRemove(win);
    }
    break;
    case KK_WIN_MSG_DESTROY:
    {
        Log(win, "win exit!\r\n");
        // if no window, quit message loop
        if (m_winList.empty())
        {
            this->Quit();
            Log(win, "app exit!\r\n");
        }
    }
    break;
    case KK_WIN_MSG_MOUSE_WHEEL:
    case KK_WIN_MSG_MOUSE_KUP:
    case KK_WIN_MSG_MOUSE_DBCLICK:
    case KK_WIN_MSG_MOUSE_KDOWN:
    {
        Log(win, "mouse [%d] (%d, %d) %d\r\n", event.msg, event.pos.x, event.pos.y, event.value);
    }
    break;
    case KK_WIN_MSG_MOVE:
        break;
    case KK_WIN_MSG_RESIZE:
    {
        // force update render DC
        auto render = win->GetRender();
        if (render != nullptr)
        {
            render->Release(); // force Init new when draw
        }
    }
    break;
    case KK_WIN_MSG_ACTIVATE:
    case KK_WIN_MSG_SHOW:
    {
        Log(win, "win state change: active %d, show %d\r\n", win->m_is_active, win->m_is_show);
    }
    break;
    default:
        Log(win, "KKApplication::OnEvent %d\n", event.msg);
        break;
    }
}

void KKApplication::Log(KKWindow *win, const char *fmt, ...)
{
    const int bufferSize = 1024 * 4;
    char buffer[bufferSize];
    memset(buffer, 0, bufferSize);
    // prefix
    sprintf_s(buffer, bufferSize, "win [%08X]: ", win->GetWinHandle());
    // user data
    va_list args;
    va_start(args, fmt);
    vsnprintf(buffer + 16, bufferSize - 16, fmt, args);
    va_end(args);
    // out
    printf("%s", buffer);
}
std::vector<KKRect> _calc_ui_child_flex_rect(KKUIBase *pUI)
{
    std::vector<KKRect> subRcList;

    int ui_size = pUI->children.size();
    int growMax = 0;
    int growFixedSpace[2]{0, 0};
    // calc original rect
    for (int i = 0; i < ui_size; i++)
    {
        auto subUi = pUI->children[i];
        auto width = std::get<int>(subUi->properties[0].css[KK_CSS_PROPERTY_WIDTH]);
        auto height = std::get<int>(subUi->properties[0].css[KK_CSS_PROPERTY_HEIGHT]);
        auto left = std::get<int>(subUi->properties[0].css[KK_CSS_PROPERTY_LEFT]);
        auto top = std::get<int>(subUi->properties[0].css[KK_CSS_PROPERTY_TOP]);
        KKRect rc{left, top, left + width, top + height};
        // printf("Origin [%d] => Rect(%d,%d,%d,%d)\n", i, left, top, width, height);

        growMax += subUi->layout.flex_grow;
        if (subUi->layout.flex_grow == 0)
        {
            if (pUI->layout.flex_direction_row)
            {
                growFixedSpace[0] += rc.x2 - rc.x1;
                if ((rc.y2 - rc.y1) > growFixedSpace[1])
                {
                    growFixedSpace[1] = rc.y2 - rc.y1;
                }
            }
            else
            {
                growFixedSpace[1] += rc.y2 - rc.y1;
                if ((rc.x2 - rc.x1) > growFixedSpace[0])
                {
                    growFixedSpace[0] = rc.x2 - rc.x1;
                }
            }
        }
        subRcList.push_back(rc);
    }

    if (pUI->layout.type == KK_LAYOUT_TYPE_NONE)
    {
        return subRcList;
    }

    // flex
    double scale = 0;
    auto x = pUI->m_rect_content.x1 - pUI->m_rect.x1;
    auto y = pUI->m_rect_content.y1 - pUI->m_rect.y1;
    // no grow
    if (growMax == 0)
    {
        // x
        switch (pUI->layout.flex_content_align_x)
        {
        case KK_TEXT_ALIGN_LEFT:
            break;
        case KK_TEXT_ALIGN_CENTER:
        {
            x += (pUI->m_rect_content.x2 - pUI->m_rect_content.x1 - growFixedSpace[0]) / 2;
        }
        break;
        case KK_TEXT_ALIGN_RIGHT:
        {
            x += (pUI->m_rect_content.x2 - pUI->m_rect_content.x1 - growFixedSpace[0]);
        }
        break;
        default:
            break;
        }
        // y
        switch (pUI->layout.flex_content_align_y)
        {
        case KK_TEXT_ALIGN_LEFT:
            break;
        case KK_TEXT_ALIGN_CENTER:
        {
            y += (pUI->m_rect_content.y2 - pUI->m_rect_content.y1 - growFixedSpace[1]) / 2;
        }
        break;
        case KK_TEXT_ALIGN_RIGHT:
        {
            y += (pUI->m_rect_content.y2 - pUI->m_rect_content.y1 - growFixedSpace[1]);
        }
        break;
        default:
            break;
        }
    }
    else
    {
        if (pUI->layout.flex_direction_row)
        {
            scale = ((pUI->m_rect_content.x2 - pUI->m_rect_content.x1) - growFixedSpace[0]) * 1.0 / growMax;
        }
        else
        {
            scale = ((pUI->m_rect_content.y2 - pUI->m_rect_content.y1) - growFixedSpace[1]) * 1.0 / growMax;
        }
    }

    // calc grow rect
    for (int i = 0; i < ui_size; i++)
    {
        auto rc = subRcList[i];
        auto subUi = pUI->children[i];
        if (pUI->layout.flex_direction_row)
        {
            auto w = rc.x2 - rc.x1;
            if (subUi->layout.flex_grow > 0)
            {
                w = (int)std::round(subUi->layout.flex_grow * scale);
            }
            subRcList[i].x1 = x;
            subRcList[i].x2 = x + w;
            x += w;
            subRcList[i].y1 = y;
            subRcList[i].y2 = y + rc.y2 - rc.y1;
            // y
            switch (pUI->layout.flex_content_align_y)
            {
            case KK_TEXT_ALIGN_LEFT:
                break;
            case KK_TEXT_ALIGN_CENTER:
            {
                subRcList[i].y1 = y + (pUI->m_rect_content.y2 - pUI->m_rect_content.y1 - (rc.y2 - rc.y1)) / 2;
                subRcList[i].y2 = subRcList[i].y1 + rc.y2 - rc.y1;
            }
            break;
            case KK_TEXT_ALIGN_RIGHT:
            {
                subRcList[i].y1 = y + (pUI->m_rect_content.y2 - pUI->m_rect_content.y1 - (rc.y2 - rc.y1));
                subRcList[i].y2 = subRcList[i].y1 + rc.y2 - rc.y1;
            }
            break;
            case KK_TEXT_ALIGN_FLEX:
            {
                subRcList[i].y1 = y;
                subRcList[i].y2 = subRcList[i].y1 + pUI->m_rect_content.y2 - pUI->m_rect_content.y1;
            }
            default:
                break;
            }
        }
        else
        {
            subRcList[i].x1 = x;
            subRcList[i].x2 = x + rc.x2 - rc.x1;
            // x
            switch (pUI->layout.flex_content_align_x)
            {
            case KK_TEXT_ALIGN_LEFT:
                break;
            case KK_TEXT_ALIGN_CENTER:
            {
                subRcList[i].x1 = x + (pUI->m_rect_content.x2 - pUI->m_rect_content.x1 - (rc.x2 - rc.x1)) / 2;
                subRcList[i].x2 = subRcList[i].x1 + rc.x2 - rc.x1;
            }
            break;
            case KK_TEXT_ALIGN_RIGHT:
            {
                subRcList[i].x1 = x + (pUI->m_rect_content.x2 - pUI->m_rect_content.x1 - (rc.x2 - rc.x1));
                subRcList[i].x2 = subRcList[i].x1 + rc.x2 - rc.x1;
            }
            break;
            case KK_TEXT_ALIGN_FLEX:
            {
                subRcList[i].x1 = x;
                subRcList[i].x2 = subRcList[i].x1 + pUI->m_rect_content.x2 - pUI->m_rect_content.x1;
            }
            break;
            default:
                break;
            }
            auto h = rc.y2 - rc.y1;
            if (subUi->layout.flex_grow > 0)
            {
                h = (int)std::round(subUi->layout.flex_grow * scale);
            }
            subRcList[i].y1 = y;
            subRcList[i].y2 = y + h;
            y += h;
        }
        // printf("calc [%d] => Rect(%d,%d,%d,%d)\n", i, subRcList[i].x1, subRcList[i].y1, subRcList[i].x2 - subRcList[i].x1,
        //        subRcList[i].y2 - subRcList[i].y1);
    }

    return subRcList;
}
void _loop_render_ui(KKRenderI *render, KKWindow *win, KKUIBase *pUI, KKRect rcForce)
{
    if (pUI != nullptr)
    {
        pUI->calcRect(rcForce);
        pUI->onRender(render, win);

        // child rc
        if (pUI->children.size() == 0)
            return;
        auto rcForceList = _calc_ui_child_flex_rect(pUI);
        for (auto it = pUI->children.begin(); it != pUI->children.end(); it++)
        {
            auto rc = rcForceList[it - pUI->children.begin()];
            _loop_render_ui(render, win, *it, rc);
        }
    }
}
void KKApplication::_RenderWinUI(KKWindow *win)
{
    auto rc = win->m_rect_client;
    if ((rc.x2 - rc.x1) <= 0 || (rc.y2 - rc.y1) <= 0 || win->m_is_show == false)
    {
        return;
    }
    auto pUI = win->GetUITree();
    auto render = win->GetRender();
    if (render == nullptr || pUI == nullptr)
        return;
    // render init check
    if (!render->IsReady())
    {
        render->Init(win->GetWinHandle());
    }
    // loop render ui
    // m_render->Clear(KKColor(255, 255, 255, 255));
    _loop_render_ui(render, win, pUI, win->m_rect_client);
}